// OgreEuler class for Ogre
// Author: Kojack
// License: Do whatever you want with it.

#include <Ogre.h>

class OgreEuler
{
public:
	// Constructor which takes yaw, pitch and roll values.
	OgreEuler(Ogre::Radian y = Ogre::Radian(0.0f), Ogre::Radian p = Ogre::Radian(0.0f), Ogre::Radian r = Ogre::Radian(0.0f)):m_yaw(y),m_pitch(p),m_roll(r),m_changed(true) 
	{
	}

	// Get the Yaw angle.
	inline Ogre::Radian getYaw() 
	{
		return m_yaw;
	}

	// Get the Pitch angle.
	inline Ogre::Radian getPitch() 
	{
		return m_pitch;
	}

	// Get the Roll angle.
	inline Ogre::Radian getRoll() 
	{
		return m_roll;
	}

	// Apply a relative yaw. (Adds angle to current yaw)
	// Angles wrap around within the range 0 to 2*PI radians (0 to 360 degrees)
	inline OgreEuler &yaw(Ogre::Radian y) 
	{
		m_yaw += y; 
		if(m_yaw.valueRadians() < 0.0f)
		{
			m_yaw = fmod(m_yaw.valueRadians(), Ogre::Math::PI * 2.0f) + Ogre::Math::PI * 2.0f;
		}
		else if(m_yaw.valueRadians() > Ogre::Math::PI)
		{
			m_yaw = fmod(m_yaw.valueRadians(), Ogre::Math::PI * 2.0f);
		}
		m_changed = true; 
		return *this;
	}

	// Apply a relative pitch. (Adds angle to current pitch)
	inline OgreEuler &pitch(Ogre::Radian p) 
	{
		m_pitch += p; 
		m_changed = true; 
		return *this;
	}

	// Apply a relative roll. (Adds angle to current roll)
	inline OgreEuler &roll(Ogre::Radian r) 
	{
		m_roll += r; 
		m_changed = true; 
		return *this;
	}

	// Set the yaw.
	inline OgreEuler &setYaw(Ogre::Radian y) 
	{
		m_yaw = y; 
		m_changed = true; 
		return *this;
	}

	// Set the pitch.
	inline OgreEuler &setPitch(Ogre::Radian p) 
	{
		m_pitch = p; 
		m_changed = true; 
		return *this;
	}

	// Set the roll.
	inline OgreEuler &setRoll(Ogre::Radian r) 
	{
		m_roll = r; 
		m_changed = true; 
		return *this;
	}

	// Get a vector pointing forwards.
	inline Ogre::Vector3 getForward() 
	{
		return toQuaternion() * Ogre::Vector3::NEGATIVE_UNIT_Z;
	}

	// Get a vector pointing to the right.
	inline Ogre::Vector3 getRight() 
	{
		return toQuaternion() * Ogre::Vector3::UNIT_X;
	}

	// Get a vector pointing up.
	inline Ogre::Vector3 getUp() 
	{
		return toQuaternion() * Ogre::Vector3::UNIT_Y;
	}

	// Calculate the quaternion of a euler object.
	// The result is cached, it is only recalculated when the component euler angles are changed.
	inline Ogre::Quaternion toQuaternion() 
	{
		if(m_changed) 
		{
			m_cachedQuaternion = Ogre::Quaternion(m_yaw, Ogre::Vector3::NEGATIVE_UNIT_Y) * Ogre::Quaternion(m_pitch, Ogre::Vector3::UNIT_X) * Ogre::Quaternion(m_roll, Ogre::Vector3::NEGATIVE_UNIT_Z); 
			m_changed = false;
		}
		return m_cachedQuaternion;
	}

	// Casting operator. This allows any ogre function that wants a Quaternion to accept a OgreEuler instead.
	inline operator Ogre::Quaternion() 
	{
		return toQuaternion();
	}

	// Set the yaw and pitch to face in the given direction.
	// The direction doesn't need to be normalised.
	// Roll is unaffected.
	OgreEuler &setDirection(const Ogre::Vector3 &v)
	{
		Ogre::Vector3 d(v.normalisedCopy());
		m_pitch = asin(d.y);
		m_yaw = atan2(d.z, d.x)+Ogre::Math::PI/2.0;
		m_changed = true;

		return *this;
	}

	// Get the angular difference between the current yaw and the specified yaw.
	// Only yaw is considered, pitch and roll are ignored.
	inline Ogre::Radian getYawToDirection(const Ogre::Radian &a)
	{
		Ogre::Real angle = (a - m_yaw).valueRadians();
		if(angle>Ogre::Math::PI)
			angle = -Ogre::Math::PI*2.0f + angle;
		else if(angle<-Ogre::Math::PI)
			angle = Ogre::Math::PI*2.0f + angle;
		return Ogre::Radian(angle);
	}

	// Get the angular difference between the current yaw and the specified direction vector.
	// Only yaw is considered, pitch and roll are ignored.
	inline Ogre::Radian getYawToDirection(const Ogre::Vector3 &v)
	{
		return getYawToDirection(Ogre::Radian(atan2(v.z, v.x) + Ogre::Math::PI/2.0f));
	}

	// Get the angular difference between the current yaw and the specified euler object.
	// Only yaw is considered, pitch and roll are ignored.
	inline Ogre::Radian getYawToDirection(const OgreEuler &e)
	{
		return getYawToDirection(e.m_yaw);
	}

	// Change the yaw to face in the direction of the vector.
	// Only yaw is changed, pitch and roll are ignored.
	inline OgreEuler &yawToDirection(const Ogre::Vector3 &v)
	{
		m_yaw = getYawToDirection(v);
		m_changed = true;
		return *this;
	}

	// Change the yaw to face in the direction of the euler object.
	// Only yaw is changed, pitch and roll are ignored.
	inline OgreEuler &yawToDirection(const OgreEuler &e)
	{
		m_yaw = getYawToDirection(e);
		m_changed = true;
		return *this;
	}

	// stream operator, for printing the euler component angles to a stream
	inline friend std::ostream &operator<<(std::ostream &o, const OgreEuler &e)
	{
		o << "<Y:" << e.m_yaw << ", P:" << e.m_pitch << ", R:" << e.m_roll << ">";
		return o;
	}

protected:
	Ogre::Radian m_yaw;     // Rotation around the Y axis.
	Ogre::Radian m_pitch;   // Rotation around the X axis.
	Ogre::Radian m_roll;    // Rotation around the Z axis.
	Ogre::Quaternion m_cachedQuaternion;    // Cached quaternion equivalent of this euler object.
	bool m_changed;         // Is the cached quaternion out of date?
};
